home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / pc / PMTUT.ZIP / PMTUT002.ZIP / PMTUT.TXT < prev   
Encoding:
Text File  |  1995-10-02  |  51.5 KB  |  1,438 lines

  1.  
  2.                 Protected Mode Tutorial
  3.                        V 0.02
  4.  
  5.                 Written by Till Gerken
  6.  
  7. -------------------------------------------------------------------------------
  8.  
  9. -- INTRODUCTION --
  10.  
  11. This text contains a short and simple step-by-step tutorial for Protected Mode
  12. beginners. It shows you everything you need to program your own PM environment
  13. and is intended for those who don't have any experiences with it yet. All you
  14. need to understand this text is your brain (you have one, do you? :) ) and a
  15. bit assembler knowledge.
  16.  
  17. Do everything you want with the information contained in this document, BUT I
  18. DO NOT TAKE ANY RESPONSIBILITIES FOR DAMAGES WHICH ARE CAUSED DIRECTLY OR
  19. INDIRECTLY BY USING THE CODE SHOWN HERE. USE AT YOUR OWN RISK!!
  20.  
  21. If you are doing something commercial with my code, please send me a postcard
  22. from the place where you live (you can even send me a postcard if don't do
  23. anything with my code but don't send e-mail as a substitute for a real
  24. postcard!).
  25.  
  26. My address is:
  27.  
  28.     Till Gerken
  29.     Wiefelsteder Str. 2a
  30.     26127 Oldenburg
  31.     Germany
  32.  
  33. Internet address:
  34.  
  35.     Till.Gerken@ngosub0.ngo.ol.ni.schule.de
  36.  
  37. Fido address:
  38.  
  39.     2:2426/2190.16
  40.  
  41. Any comments, suggestions, criticism or whatever can be sent to one of these
  42. addresses.
  43.  
  44. The text will try to teach you the principles of the 80386's Protected Mode
  45. and contains the complete source for a mode switcher. This little program was
  46. made to show you the basic rules of Protected Mode code, so it is unoptimized
  47. and kept simple, but very well documented. If you have questions/suggestions
  48. for an extension, _please_ mail me.
  49.  
  50. The whole code is written in assembler, using Ideal mode TASM 2.01 syntax.
  51. Newer versions of TASM will work, too, but you'll have to add a macro
  52. to convert DWORDS to WORDS in order to compile the assembler file.
  53.  
  54. There are some figures in this text, they look better if you print them.
  55. All tabulators have to be set to 8.
  56.  
  57. Any code can also be found as a complete program in the file PMTUT.ASM
  58. (contained in this archive). Please do not rewrite the stuff contained below,
  59. it is not always as complete as needed.
  60.  
  61. To port the code in PMTUT.ASM to other assemblers like MASM, you have to notice
  62. that I used the feature of TASM to automatically group segments if an address
  63. couldn't be resolved. Look at the GDT setup in START16. You'll see something
  64. like that:
  65.         mov    [ds:code16_descriptor.base0_15],ax
  66. Using the DS prefix causes TASM to resolve this address (it is in CODE32 and
  67. can't normally be accessed from CODE16).
  68.  
  69. To port the code, add a group containing CODE16 and CODE32, change the ASSUME
  70. directive of CODE16 to link DS with the group and remove all DS prefixes. Only
  71. remember to change the lines in CODE16 containing relative offsets in CODE32,
  72. like
  73.         add    eax,offset dummy_descriptor
  74. to
  75.         add    eax,offset code32:dummy_descriptor
  76. else TASM would insert an offset relative to the group start address.
  77.  
  78. I didn't tried the things above, but it should work. Please mail me if you
  79. port this code to other assemblers, I'd really like to know how it goes on.
  80.  
  81. I'm currently thinking about splitting the text into several files, each
  82. containing information about a different topic. (like mode switching under
  83. DPMI, VCPI, XMS and RAW; Exception handling; etc.) This would made it a bit
  84. handier and easier to read. Someone has any opinion?
  85.  
  86. -------------------------------------------------------------------------------
  87.  
  88. -- GETTING STARTED --
  89.  
  90. Well, "getting started" is what this document is all about... Here you'll
  91. learn what you need to prepare for Protected Mode. There are several things
  92. you have to take care of, at first you have to check on which processor your
  93. program's currently running (trying to do PM on a 8086 may hurt... :) )
  94.  
  95. The best way to test for a 80386 is to test the processor's flag register.
  96. Since the 80386, flag bits 12-14 are used for the I/O Privilege Level (IOPL)
  97. and the Nested Task (NT) flag, so the only thing to do is to test if these bits
  98. are modifyable (the 8086, 8088, 80186 don't use these flags and set them
  99. implicitely to zero, the 80286 uses them, but they can only be modified in
  100. Protected Mode. DOS can't run in Protected Mode, so if the program runs on a
  101. 80286, it is in Real Mode, and there the flags can't be modified).
  102.  
  103.  
  104. ; checks for a 386
  105.  
  106. no386e        db    'Sorry, at least a 80386 is needed!',13,10,'$'
  107.  
  108. proc    check_processor
  109.     pushf                ; save flags
  110.     xor    ah,ah            ; clear high byte
  111.     push    ax            ; push AX onto the stack
  112.     popf                ; pop this value into the flag register
  113.     pushf                ; push flags onto the stack
  114.     pop    ax            ; ...and get flags into AX
  115.     and    ah,0f0h            ; try to set the high nibble
  116.     cmp    ah,0f0h            ; the high nibble is never 0f0h on a
  117.     je    no386            ; 80386!
  118.     mov    ah,70h            ; now try to set NT and IOPL
  119.     push    ax
  120.     popf
  121.     pushf
  122.     pop    ax
  123.     and    ah,70h            ; if they couldn't be modified, there
  124.     jz    no386            ; is no 80386 installed
  125.     popf                ; restore the flags
  126.     ret                ; ...and return
  127. no386:                    ; if there isn't a 80386, put a msg
  128.     mov    dx,offset no386e    ; and exit
  129.     jmp    err16exit
  130. endp    check_processor
  131.  
  132. ; exits with a msg
  133. ; In:    DS:DX - pointer to msg
  134.  
  135. proc    err16exit
  136.     mov    ah,9            ; select DOS' print string function
  137.     int    21h            ; do it
  138.     mov    ax,4cffh        ; exit with 0ffh as exit code
  139.     int    21h            ; good bye...
  140. endp    err16exit
  141.  
  142.  
  143. Now we have a function to determine if there is a 80386 installed and one
  144. which provides a quick and dirty exit. The second thing to be done now is
  145. to check in which mode we are running. Expanded Memory Managers like EMM386,
  146. QEMM, etc. usually switch to V86 mode to provide their services. Our little
  147. program only works in Real Mode, so another function has to be coded.
  148.  
  149. To distinguish between Real Mode and V86 mode, we have to look at the Control
  150. Register 0: bit 0 is clear when we are running in Real Mode, otherwise it is
  151. set.
  152.  
  153.  
  154. ; checks if we are running in Real Mode
  155.  
  156. nrme        db    'You are currently running in V86 mode!',13,10,'$'
  157.  
  158. proc    check_mode
  159.     mov    eax,cr0            ; get CR0 to EAX
  160.     and    al,1            ; check if PM bit is set
  161.     jnz    not_real_mode        ; yes, it is, so exit
  162.     ret                ; no, it isn't, return
  163. not_real_mode:
  164.     mov    dx,offset nrme
  165.     jmp    err16exit
  166. endp    check_mode
  167.  
  168.  
  169. Now we can be sure that nothing will disturb us while setting up for Protected
  170. Mode. DPMI and VCPI (even BIOS) can be used for mode switching too, but this is
  171. left as an exercise to you. (the interface however is described below)
  172.  
  173. Please notice that I used MOV EAX,CR0 in this example. Jerzy Tarasiuk pointed
  174. out that this is not allowed in a Protected Mode environment, especially not
  175. on a 286. If the program doesn't work on your computer, try SMSW AX. This
  176. instruction is only supported on the 386/486 for compatibility reasons and
  177. shouldn't be used any more, but it works in any environment.
  178.  
  179. When switching to Protected Mode, you also have to change MOV CR0,EAX to
  180. LMSW AX.
  181.  
  182. Please note that you can use LMSW only to get _into_ Protected Mode. The
  183. instruction cannot be used to get _out_ of Protected Mode. To handle this
  184. weird behaviour, you have to force the processor to enter shutdown mode and
  185. then reset it. This is, however, not described here, as it would be a too
  186. complex thing for a beginner's tutorial. ;)
  187.  
  188. -------------------------------------------------------------------------------
  189.  
  190. -- TABLES AND DESCRIPTORS AND SELECTORS AND  L O T S  OF CONFUSING STUFF --
  191.  
  192. Before the actual mode switch, we have to set up a few tables and descriptors.
  193. What I'm talking about is the GDT, the LDT and the IDT.
  194.  
  195. The GDT is the Global Descriptor Table and contains basic segment descriptors.
  196. These segment descriptors are keeping information about different parts of
  197. the memory. In Real Mode, one segment is 64kb big followed by the next segment
  198. in a 16 byte distance. In Protected Mode however, you can decide yourself
  199. where to put a segment. Every segment can be as big as 4Gb (in words: four
  200. giga-bytes!). The LDT is optional and contains segment descriptors, too, but
  201. these are usually used for applications. An Operating System, for example,
  202. could set up the GDT with it's own system descriptors and for every task a LDT
  203. which contains the application descriptors.
  204.  
  205. The LDT is a descriptor table like the GDT. It's usage is to provide different
  206. tasks different memory-layouts. In our program, LDTs aren't needed.
  207. The IDT contains the interrupt descriptors. These are used to tell the
  208. processor where to find the interrupt handlers. It contains one entry per
  209. interrupt, just like in Real Mode, but the format of these entries is totally
  210. different.
  211.  
  212. Here is the basic format of these descriptors:
  213.  
  214. Segment Descriptor
  215. ------------------
  216.  
  217. 15   14   13   12   11   10   09   08   07   06   05   04   03   02   01   00
  218. [        Base address 31:24         ]   [G]  [D]  [0] [AVL] [Sg. length 19:16]
  219. [P] [DPL] [DT] [        Type        ]   [        Base address 23:16         ]
  220. [                            Base address 15:00                             ]
  221. [                           Segment length 15:00                            ]
  222.  
  223. As you can see in this figure, the basic segment descriptor has a size of
  224. 4*16=64 bits.
  225. To give you a help understanding the structure (my ASCII graphics are not very
  226. good at all), here is the same a bit more compact in assembler:
  227.  
  228.  
  229. ; contains a segment descriptor
  230.  
  231. struc segment_descriptor
  232.   seg_length0_15    dw    ?    ; low word of the segment length
  233.   base_addr0_15        dw    ?    ; low word of base address
  234.   base_addr16_23    db    ?    ; low byte of high word of base addr.
  235.   flags            db    ?    ; segment type and misc. flags
  236.   access        db    ?    ; highest nibble of segment length
  237.                       ; and access flags
  238.   base_addr24_31    db    ?    ; highest byte of base address
  239. ends segment_descriptor
  240.  
  241.  
  242. Using this structure makes handling the GDT and LDT much easier. The same
  243. applies to the IDT, but here the format is different:
  244.  
  245. 15   14   13   12   11   10   09   08   07   06   05   04   03   02   01   00
  246. [                               Offset 31:16                                ]
  247. [P]  [ DPL ]   [0]  [1]  [1]  [1]  [0]  [0]  [0]  [0]  [0]  [0]  [0]  [0]  [0]
  248. [                              Selector 00:15                               ]
  249. [                               Offset 00:15                                ]
  250.  
  251. And, as above, the same in assembler:
  252.  
  253.  
  254. ; contains an interrupt descriptor
  255.  
  256. struc interrupt_descriptor
  257.   offset0_15    dw    ?        ; low word of handler offset
  258.   selector0_15    dw    ?        ; segment selector
  259.   zero_byte    db    0        ; unused in this descriptor format
  260.   flags        db    ?        ; flag-byte
  261.   offset16_31    dw    ?        ; high word of handler offset
  262. ends interrupt_descriptor
  263.  
  264.  
  265. Now you know the segment and interrupt descriptor format. But what to fill in?
  266. Good question. Before explaining you this, I'll give you a short table where
  267. all mentioned bit names are described:
  268.  
  269. Bit name    Meaning
  270. -------------------------------------------------------------------------------
  271. G        Granularity.
  272.             G=0  =>  1 Byte granularity
  273.             G=1  =>  4k granularity
  274.         This bit specifies the granularity of the segment. If the bit
  275.         is clear, the length field in the descriptor reflects the
  276.         real length of the segment in bytes. If the bit is set, you
  277.         have to multiply the length field in the descriptor by 4096
  278.         to get the real length in bytes.
  279. D        Default Operand Size.
  280.             D=0  =>  16 bit operands
  281.             D=1  =>  32 bit operands
  282.         This bit specifies the default operand size which has to be
  283.         used by special opcodes (like REP xxx). If the bit is clear,
  284.         the default operand size is 16 bit and the processor behaves
  285.         similar to a 80286. If the bit is set, the default operand
  286.         size is 32 bit. D=0 does not mean you can't use 32 bit
  287.         instructions, it only affects the default operand sizes.
  288. AVL        Available for System.
  289.         This bit is not used in 80286/80386/80486 machines. If
  290.         somebody has information about how it is used on Pentium
  291.         machines, please mail me. For now, better keep it to zero to
  292.         keep compatibility. However, if your program only runs on
  293.         machines lower than Pentium, you can use it as a mark for your
  294.         own software or whatever.
  295. P        Presence.
  296.             P=0  =>  segment is not present (or invalid)
  297.             P=1  =>  segment is present and valid
  298.         With this bit you can easily implement a virtual memory
  299.         manager (VMM). If the application wants to allocate more memory
  300.         than available, save the least used segment (determined with
  301.         help of the A bit) to disk, then clear the P bit in its
  302.         descriptor. The next access to that segment will be followed by
  303.         a General Protection Fault. Catch the fault, reload the segment
  304.         into memory and set its P bit. Done. The processor checks only
  305.         the P bit before generating the General Protection Fault, so
  306.         if P is set to zero, the rest of the descriptor is available
  307.         to keep information for your VMM.
  308. DPL        Descriptor Privilege Level.
  309.             0 <= DPL <= 3
  310.         The DPL bits contain the Descriptor Privilege Level. The
  311.         Privilege Level has a range from 0 (highest privilege level)
  312.         to 3 (lowest privilege). If a program tries to access a
  313.         segment with a higher privilege level than its own, the
  314.         processor will generate a General Protection Fault.
  315.         REMARK: Every time I speak of Privilege Levels in this text,
  316.         I mean that HIGH Privilege Level is a LOW number in DPL,
  317.         LOW Privilege Level is a HIGH number in DPL.
  318.         Example: segment 1: DPL=1 \
  319.                         -> segment 1 is more privileged
  320.              segment 2: DPL=3 /
  321. DT        Descriptor Type.
  322.             DT=0 =>  System Descriptor (System-Segment or Gate)
  323.             DT=1 =>  Application Descriptor (Data or Code)
  324.         If this bit is clear, the Descriptor describes a segment that
  325.         is (a) available for the System Software, or (b) a
  326.         Gate-Descriptor.
  327. Type        Segment type.
  328.         These four bits select the segment type.
  329.         
  330.         Bit    3   2   1   0    Type    Description
  331.         Name    T   E   W   A
  332.             0   0   0   0    Data    read-only
  333.             0   0   0   1    Data    read-only, accessed
  334.             0   0   1   0    Data    read/write
  335.             0   0   1   1    Data    read/write, accessed
  336.             0   1   0   0    Data    read-only, expand down
  337.             0   1   0   1    Data    read-only, exp. down, acc.
  338.             0   1   1   0    Data    read-write, expansion down
  339.             0   1   1   1    Data    read-write, exp. down, acc.
  340.             1   0   0   0    Code    exec-only
  341.         ---------------------------------------------------------------
  342.         Name    T   C   R   A
  343.             1   0   0   1    Code    exec-only, accessed
  344.             1   0   1   0    Code    exec-read
  345.             1   0   1   1    Code    exec-read, accessed
  346.             1   1   0   0    Code    exec-only, conforming
  347.             1   1   0   1    Code    exec-only, conf., acc.
  348.             1   1   1   0    Code    exec-read, conforming
  349.             1   1   1   1    Code    exec-read, conf., acc.
  350.             
  351.         Read-only means that you are only allowed to read this segment.
  352.         Read-write means that you can read and write from/to the
  353.         segment.
  354.         Exec-only segments can only be executed, but no read-access is
  355.         allowed.
  356.         Exec-read segments can be read and executed. Unlike as in Real
  357.         Mode, you aren't allowed to use self-modifying code. A way
  358.         around this can be found a few lines ahead in this text.
  359.         The Accessed bit is set everytime a program tried to access
  360.         this segment _and_ the bit isn't already set. If you want to
  361.         figure out which segment to swap (the famous VMM example),
  362.         increase a counter if the A bit is set and then clear this bit.
  363.         The segment with the lowest counter position can safely be
  364.         swapped out. BUT WATCH OUT: If the A bit is set, a program
  365.         might run in this segment! Swapping these segments may hurt!
  366.         Expansion Direction is a weird thing. If the bit is clear, the
  367.         Expansion Direction is upwards, that means the segment grows
  368.         upwards. To grow it, increase the length. You are allowed to
  369.         access every address that is
  370.                 0 <= Address <= Limit
  371.         Limit means the actual length of the segment. If the segments
  372.         Granularity is set to 0, the limit is equal to the length.
  373.         But if Granularity is set to 1, you first have to multiply the
  374.         length by 4096 (4k) to get the length.
  375.         If the bit is set however, welcome to hell. Now the Expansion
  376.         Direction is _downwards_, that means to grow the segment,
  377.         you'll have to _decrease_ the Length. You are allowed to
  378.         access every address that is
  379.             G=0 : Limit-1 <= Address <= 0ffffh
  380.             G=1 : Limit-1 <= Address <= 0ffffffffh.
  381.         Because of the 4G Wrap-Around, these addresses are just the
  382.         ones that would cause a General Protection Fault if E would be
  383.         zero.
  384.         Conforming means that a segment with C=1 can call another
  385.         segment with a lower or equal Privilege Level. The Current
  386.         Privilege Level however isn't changed!
  387.         If you call directly from a segment with C=0 (and not through
  388.         a Task-Gate) to a segment that has another Privilege Level
  389.         than the Current Privilege Level, a General Protection Fault
  390.         follows.
  391.         
  392. That wasn't too hard, wasn't it? At first all this might look a bit confusing,
  393. but when you look at the code, it will be that simple...
  394. So the only thing left before starting to do the _real_ thing (no, not
  395. drinking Coke, I mean coding! :) ) is to explain what a Selector is:
  396. A Selector selects something, and this something are Segment Descriptors!
  397. The format is simple enough to understand:
  398.  
  399. 15   14   13   12   11   10   09   08   07   06   05   04   03   02   01   00
  400. [               Pointer into a Descriptor Table              ]   TI   [ RPL ]
  401.  
  402. RPL is the Requested Privilege Level. If the Descriptor in the Descriptor
  403. Table has a higher Privilege Level than RPL, a General Protection Fault will
  404. be caused.
  405. TI selects the Descriptor Table to get the Descriptor from : TI=0 means GDT,
  406. TI=1 means LDT.
  407. The pointer contains the offset into the Descriptor Table where the wanted
  408. Descriptor is to find.
  409.  
  410. -------------------------------------------------------------------------------
  411.  
  412. -- NOW FOR THE FUN STUFF!! --
  413.  
  414. If you understood the above, the rest is easy to do: set up the appropriate
  415. tables, then switch to PM. EASY!!!
  416. At first, we declare the two segments where to put all data and code in:
  417.  
  418.  
  419. segment code16 para public use16    ; <- the 16-bit code and data segment
  420. assume cs:code16, ds:code16
  421.  
  422. ends    code16
  423.  
  424. segment code32 para public use32    ; <- the 32-bit code and data segment
  425. assume cs:code32, ds:code32
  426.  
  427. ends    code32
  428.  
  429.  
  430. There is only one little bug with these two segments: both are code segments.
  431. If you listened carefully, you'd remember that in Protected Mode it is not
  432. allowed to write to a code segment. Well, where's the problem? (I hear them
  433. saying: let's only use static data!! :) ) Everytime your program would try
  434. to write to data located in one of these segments, a General Protection Fault
  435. will occur. What about an extra 32-bit data segment? Hmm, nice, but not the
  436. best way. Here's the probably easiest way to have code _and_ data together in
  437. one segment, even allowing self-modifying code: we have to create a so called
  438. "alias"-segment. This segment isn't really a new segment, it's just another
  439. descriptor in the GDT. The descriptor points to the same memory that is defined
  440. in the code segment descriptor. The only thing that we have to take care of now
  441. is that CS is loaded with the _code_ selector and DS, ES, FS and GS are loaded
  442. with the _data_ selector.
  443.  
  444.  
  445. ; GDT data
  446.  
  447. gdt_reg        dw    gdt_size,0,0
  448. dummy_dscr    segment_descriptor    <0,0,0,0,0>
  449. code32_dscr    segment_descriptor    <0ffffh,0,0,9ah,0cfh,0>
  450. data32_dscr    segment_descriptor    <0ffffh,0,0,92h,0cfh,0>
  451. core32_dscr    segment_descriptor    <0ffffh,0,0,92h,0cfh,0>
  452. code16_dscr    segment_descriptor    <0ffffh,0,0,9ah,0,0>
  453. data16_dscr    segment_descriptor    <0ffffh,0,0,92h,0,0>
  454. gdt_size=$-(offset dummy_dscr)
  455.  
  456.  
  457. The first line contains three words (better: one word and one dword) that
  458. contain information for the GDT register. To inform the CPU where our GDT
  459. is located in memory, we have to use the LGDT instruction. This instruction
  460. sets an internal CPU register with the data pointed to by the instruction
  461. parameter. The format of this data is
  462.  
  463.     - GDT size in bytes (word)
  464.     - GDT base address (dword)
  465.  
  466. so to set up the register, we have to use the following line:
  467.  
  468.     lgdt    [fword ds:gdt_reg]
  469.  
  470. And the same for the IDT:
  471.  
  472.     lidt    [fword ds:idt_reg]
  473.  
  474. In our GDT, we have defined six descriptors: 32-bit code (4G size, 32-bit
  475. operands, code type), 32-bit data (4G size, 32-bit operands, data type),
  476. 32-bit core (4G size, 32-bit operands, data type), 16-bit code (64k size,
  477. 16-bit operands, code type) and finally 16-bit data (64k size, 16-bit operands,
  478. data type).
  479.  
  480. Whoops, there's one descriptor missing: the dummy descriptor! Why do we have to
  481. include something like this? Good question! This descriptor can't be used and
  482. has to be set to zero. But that doesn't explain why it is included! The LDT
  483. definitely does _not_ have something like this...
  484.  
  485. The reason is the concept of the _Protected_ Mode. The CPU provides several
  486. protection mechanisms, and one of them is the "invalid" (zero) descriptor.
  487. If a segment is loaded with a zero-descriptor, every try to access memory
  488. through it will be followed by a General Protection Fault, so it can be used
  489. as a "marker". This is handy for debuggers if they want to find out when a
  490. segment register is used, but there are lots of other possibilities to take
  491. advantage of this feature.
  492.  
  493. A second thing is that the CPU validates every selector before it is loaded
  494. into a segment register. This means, that if you want to load a Real Mode
  495. segment address (like 1234h) into a segment register, the CPU checks in the GDT
  496. if there is a valid descriptor. At offset 1234h, there probably won't, so again
  497. a General Protection Fault is generated. In V86 Mode however, the processor
  498. works with segment addresses like that. To solve this problem, the CPU saves
  499. every segment register before calling an interrupt handler and loads them with
  500. the zero selector. The Protected Mode interrupt handler won't notice if it has
  501. been called from Protected or V86 Mode, so one handler will work in both modes.
  502.  
  503. Now, disable interrupts (a Real Mode Interrupt in Protected Mode does you no
  504. good, a Protected Mode Interrupt with no IDT may be even worse!),
  505.  
  506.         cli
  507.  
  508. and switch to Protected Mode:
  509.  
  510.         mov    eax,cr0
  511.         or    al,1
  512.         mov    cr0,eax
  513.  
  514. (you may use LMSW AX, too)
  515.  
  516. The next thing is dirty but there's no way around it:
  517.  
  518.         db    0eah
  519.         dw    offset start32, code32_idx
  520.  
  521. 0EAh is the opcode for JMP FAR. If you use the instruction JMP FAR, TASM tries
  522. to resolve the jump with the optimal opcode, but we need JMP FAR to flush the
  523. Instruction Prefetch Queue and to load CS with the new Protected Mode
  524. Descriptor. This has to be done because the CPU doesn't set its descriptor
  525. caches and the Instruction Prefetch Queue might contain instructions decoded in
  526. a way only valid for Real Mode.
  527.  
  528. After this, the CPU is setup for Protected Mode and starts execution at the
  529. label START32. There we just load the other segment registers with their
  530. Protected Mode selectors and call MAIN.
  531.  
  532. The rest is easy.
  533.  
  534. -------------------------------------------------------------------------------
  535.  
  536. -- MISSING PARTS IN THE DEMO SOURCE --
  537.  
  538. There are lots of features not contained in the sample source. I've done this
  539. to keep the program as simple as possible and to demonstrate as much as
  540. possible, so there is an interrupt-system missing. This thing should intercept
  541. every interrupt and redirect them to Real Mode. Second, a routine to toggle
  542. the A20 address line should be added. Mail me if you want something to be
  543. included or if you already coded something for it. You can also look at Tran's
  544. PMODE package. It contains a complete Protected Mode header with complete
  545. environment management. You won't notice what it does, you can start coding
  546. as if you were in Real Mode, it is very powerful. However, I encourage you to
  547. write your own Protected Mode system sometime, it helps to understand the
  548. principles.
  549.  
  550. -------------------------------------------------------------------------------
  551.  
  552. -- SOME CODING TIPS FOR PROTECTED MODE --
  553.  
  554. This section just can't explain all of the advantages of Protected Mode, better
  555. buy a good book, but some of them I'll show you here.
  556.  
  557. 1. To use the JMP FAR back to a 16-bit Real Mode segment from a 32-bit segment,
  558.    you have to use
  559.  
  560.     db    0eah
  561.     dw    offset real_mode_proc, 0 , segment_selector
  562.                       ^^^
  563.  
  564.    This little zero-word can cost some time of debugging.
  565.  
  566. 2. You can use _every_ register as an index.
  567.  
  568.     mov    eax,[edx]
  569.  
  570. 3. You can use displacements _and_ factors in pointers.
  571.  
  572.     mov    eax,[ecx*8+edx]
  573.  
  574.    All factors have to be powers of 2.
  575.  
  576. 4. Sometimes this feature can be used for quick multiplies:
  577.  
  578.     lea    eax,[eax*8+eax]    -> EAX=EAX*9
  579.  
  580. 5. IMUL accepts immediates:
  581.  
  582.     imul    ecx,5            -> ECX=ECX*5
  583.  
  584. 6. IMUL accepts immediates _and_ a register:
  585.  
  586.     imul    eax,ecx,5        -> EAX=ECX*5
  587.  
  588. 7. Extended form of SHR/SHL to shift across registers:
  589.  
  590.     shrd    eax,edx,5
  591.     shld    eax,edx,5
  592.  
  593.    In this example, the 5 bits that become free in EAX will be filled with
  594.    bits of EDX. EDX is not modified.
  595.  
  596. 8. This feature can be used to get rid of one MOV instruction:
  597.  
  598.    Assume ECX contains a linear address that you want to convert into a
  599.    segment:offset notation. Normally, you would use something like this:
  600.  
  601.     mov    eax,ecx
  602.     shr    eax,4            ; EAX now contains segment
  603.     and    ecx,0fh            ; ECX now contains offset
  604.  
  605.    With the SHRD instruction, you can modify it to
  606.  
  607.     shld    eax,ecx,28        ; EAX now contains segment
  608.     and    ecx,0fh            ; ECX now contains offset
  609.  
  610.    REMEMBER: The CPU only uses the 5 lower bits of the shift factor, so
  611.          shld eax,ecx,32 will _not_ copy ECX to EAX!!!
  612.  
  613. 9. You can push immediates:
  614.  
  615.     push    12345
  616.  
  617. -------------------------------------------------------------------------------
  618.  
  619. -- FAULTS, TRAPS AND EXCEPTIONS --
  620.  
  621. Below is a complete list of the Faults, Traps and Exceptions that may occur.
  622. If somebody has information about Exception 17 (Arrangement Error), please
  623. mail me.
  624.  
  625. No.    Name        Type        Error Code    Cause
  626. ----------------------------------------------------------------------------
  627. 0    Division by    Fault        no        Someone tried to
  628.     Zero                        divide by zero. Same
  629.                             as in Real Mode
  630. 1    Single Step    Trap,Fault    no        This interrupt is
  631.                             called after each
  632.                             instruction if the
  633.                             Trap Flag is set
  634. 2    Non Maskable    Abort        no        Heavy hardware failure.
  635.     Interrupt (NMI)                    Same as in Real Mode.
  636. 3    Breakpoint    Trap        no        Used for debugging
  637.                             purposes. Called by
  638.                             special INT3 opcode.
  639. 4    Overflow    Trap        no        Called if INTO is
  640.                             executed and the
  641.                             Overflow Bit is set.
  642. 5    Bound Range    Fault        no        BOUND failed
  643.     Exceeded
  644. 6    Invalid Opcode    Fault        no        CPU found an invalid
  645.                             opcode. Same as in
  646.                             Real Mode.
  647. 7    Coprocessor    Fault        no        Called if CPU tries to
  648.     Not Available                    execute ESC or WAIT and
  649.                             EM bit is clear.
  650. 8    Double Fault    Abort        yes (always 0)    An exception occured
  651.                             while another exception
  652.                             handler is active.
  653. 9    Coprocessor    Abort        no        The middle operand
  654.     Segment Overrun                    of a FPU instruction
  655.                             can't be accessed.
  656.                             Dunno what this should
  657.                             be, i486 doesn't has
  658.                             this exception any
  659.                             more.
  660. 10    Invalid TSS    Fault        yes        Tried to switch to a
  661.                             task with an invalid
  662.                             TSS.
  663. 11    Segment not    Fault        yes        Someone tried to access
  664.     Present                        a segment that had it's
  665.                             Present bit clear.
  666. 12    Stack Exception    Fault        yes        Called if stack exceeds
  667.                             it's limits or if
  668.                             selector for SS is
  669.                             invalid
  670. 13    General        Fault        yes        Someone tried to access
  671.     Protection Fault                invalid, protected or
  672.                             not-present data.
  673. 14    Page Fault    Fault        yes        Called if paging is
  674.                             enabled and and an
  675.                             access to an invalid,
  676.                             protected or
  677.                             not-present page
  678.                             occured.
  679. 16    Coprocessor    Fault        no        The FPU saw that it
  680.     Error                        was doing something
  681.                             totally wrong... :)
  682. 17    Arrangement    ????        ??        This exception occurs
  683.     Error                        only if AC=1, AM=1 and
  684.                             CPL=3. If memory isn't
  685.                             accessed at integral
  686.                             addresses, EXCP17 is
  687.                             generated. (see table
  688.                             below)
  689. 0..255    Software    Trap        no        If you call one of
  690.     Interrupts                    these interrupts from
  691.                             your program (INT xx),
  692.                             they are handled like
  693.                             Traps.
  694.  
  695. Faults are documented and recoverable errors. The return address for the IRET
  696. instruction (CS:EIP) points to the opcode that caused the Fault. Some Faults
  697. have an error code on the stack (see below). To solve the problem, the handler
  698. only has to read in the failed opcode and react on it.
  699.  
  700. Traps are interrupts that are caused by your program (INT xx instruction) or
  701. by a debugging mechanism (INT3 or Trap Mode). An error code is never generated.
  702. CS:EIP points to the opcode following the one that caused the Trap.
  703.  
  704. Aborts are only caused if the system tables (GDT, IDT, LDT) are invalid or
  705. if there was a hardware failure. They don't allow you to return to your
  706. program, nor there's an error code.
  707.  
  708. The format of the error code:
  709.  
  710. 15   14   13   12   11   10   09   08   07   06   05   04   03   02   01   00
  711. [                Reserved                    ]
  712. [            Selector                 ]   TI   IDT  EXT
  713.  
  714. Bit name    Meaning
  715. ------------------------------------------------------------------
  716. Selector    Selector of segment where the error occured
  717. TI        Table Indicator (applies only if IDT=0)
  718.             TI=0 - Selector from GDT
  719.             TI=1 - Selector from LDT
  720. IDT        Interrupt Descriptor
  721.             IDT=0 - No Interrupt Gate
  722.             IDT=1 - Selector from IDT
  723. EXT        External
  724.             EXT=0 - Program caused Exception
  725.             EXT=1 - Exception caused by external event
  726.  
  727. Arrangement Check Errors
  728.  
  729. Data Type                Address has to be dividable by
  730. ----------------------------------------------------------------------
  731. Word                    2
  732. Double Word                4
  733. Floating Point, Single Precision    4
  734. Floating Point, Double Precision    8
  735. Floating Point, Extended Precision    8
  736. Selector                2
  737. 48-Bit Farpointer            4
  738. Contents of GDTR and IDTR (48 Bits)    4
  739. 32-Bit Farpointer            2
  740. 32-Bit Address                4
  741. Bitstrings                4
  742. FPU Environment Blocks or FPU State    4 or 2, depending on Operand Length
  743.  
  744. An example how to handle these exceptions is not yet included in the demo
  745. source. If someone would like to see more about this -> mail me! (as you might
  746. have noticed, I definitely WANT your mails! :) )
  747.  
  748. -------------------------------------------------------------------------------
  749.  
  750. -- USEFUL INTERRUPTS --
  751.  
  752. In this section I'll give you some useful interrupt calls that provide handy
  753. information or services for Protected Mode.
  754.  
  755. Format of the entries:
  756.  
  757. Function name
  758. Interrupt number (or CALL address) in hexadecimal numbers
  759. Input registers
  760. Return registers
  761. Notes
  762.  
  763.  
  764. Func:    Get RAM Size
  765. Call:    INT 12h
  766. Input:    ---
  767. Return:    AX - Memory size in kb
  768. Notes:    Returns only size of conventional memory
  769.  
  770.  
  771. Func:    Move Block (AH=87h)
  772. Call:    INT 15h
  773. Input:    AH=87h
  774.     CX=Number of Words in Buffer
  775.     ES:SI=Address of Descriptor Table
  776. Return:    CY=0 - ok
  777.     CY=1 - error
  778.     AH=Status
  779. Notes:    Transfers a block of max. 64k (max. CX=8000h) via Protected Mode
  780.     to any memory location.
  781.     ES:SI -> Dummy            <--,
  782.          Table begin         --'
  783.          Source Descriptor
  784.          Destination Descriptor
  785.          BIOS Codesegment
  786.          Stacksegment
  787.     Every entry is 8 bytes long. First entry has to be set to zero.
  788.     Entry structure: - Segment size (word)
  789.              - Low Word 24-bit Segment Address (word)
  790.              - High Byte 24-bit Segment Address (byte)
  791.              - Access Flag (byte, =93h)
  792.              - Reserved (word)
  793.     The last two entries have to be set to zero.
  794.     Error code in AH:    00 - Transfer completed without error
  795.                 01 - RAM Parity Error
  796.                 02 - Exception
  797.                 03 - A20 failure
  798.  
  799.  
  800. Func:    Extended Memory Size (AH=88h)
  801. Call:    INT 15h
  802. Input:    AH=88h
  803. Return:    CY=0 - ok
  804.     AX=ext. memory size in kb
  805.     CY=1 - error
  806.     AH=error code (80h - invalid command, 86h - function not supported)
  807. Notes:    Function returns extended memory size stored at address 20h and 31h
  808.     in CMOS RAM of clock chip. Extended Memory can only be used if
  809.     base memory is at least 512k big. If HIMEM.SYS is loaded, the function
  810.     returns 0.
  811.  
  812.  
  813. Func:    Virtual Mode (AH=89h)
  814. Call:    INT 15h
  815. Input:    AH=89h
  816.     BH=first PIC start
  817.     BL=second PIC start
  818.     CX=ofsfet of code segment
  819.     ES:SI=pointer to Descriptor Table
  820. Return:    CY=0 - ok
  821.     AH=0
  822.     CY=1 - error
  823.     AH=0ffh
  824. Notes:    Function destroys all registers. ES:SI points to Descriptor Table.
  825.     ES:SI -> Dummy            <--,
  826.          Table begin         --'
  827.          Interrupt Table
  828.          User Data Segment (DS)
  829.          User Extra Segment (ES)
  830.          User Stack Segment (SS)
  831.          User Code Segment (CS)
  832.          Internal Code Segment
  833.     Entries structured like in function Move Block (AH=87h). Dummy has to
  834.     be set to zero. Third entry points to IDT built by program (Real Mode
  835.     structure). Last Descriptor has to be set to zero. BH contains mappings
  836.     for first PIC (start of first 8 hardware interrupts, i.e. BH=8 if IRQ0
  837.     should be shifted to IRQ8). BL contains mappings for second PIC.
  838.     Carry flag has to be cleared before function call. CX contains code
  839.     segment where execution in V86 should start. After function call no
  840.     BIOS is available, return to Real Mode only by resetting CPU.
  841.  
  842. -------------------------------------------------------------------------------
  843.  
  844. -- THE VIRTUAL DMA SPECIFICATION (VDS) --
  845.  
  846. The VDS provides services to use DMA transfers in Protected Mode with enabled
  847. paging mechanism.
  848.  
  849. I am not sure about the information contained here. Please mail me if there are
  850. errors. I hoped it would be handy for you, so it is included.
  851.  
  852. Error codes used in all functions:
  853.         01h    region not in contigous memory
  854.         02h    region crossed a physical alignment boundary
  855.         03h    unable to lock pages
  856.         04h    no buffer available
  857.         05h    region too large for buffer
  858.         06h    buffer currently in use
  859.         07h    invalid memory region
  860.         08h    region wasn't locked
  861.         09h    number of physical pages greater than table length
  862.         0ah    invalid buffer ID
  863.         0bh    copy out of buffer range
  864.         0ch    invalid DMA channel number
  865.         0dh    disable count overflow
  866.         0eh    disable count underflow
  867.         0fh    function not supported
  868.         10h    reserved flag bits set in DX
  869.  
  870. Sometimes Descriptor Tables are needed (DMA Descriptor Structure - DDS).
  871. Format as following:
  872.  
  873.     Offset    Bytes    Meaning
  874.     00h    4    size of region
  875.     04h    4    region offset
  876.     08h    2    region segment
  877.     0ah    2    buffer ID
  878.     0ch    4    linear address
  879.  
  880. Some functions use an extended format (EDDS):
  881.  
  882.     Offset    Bytes    Meaning
  883.     00h    4    size of region
  884.     04h    4    region offset
  885.     08h    2    region segment
  886.     0ah    2    reserved
  887.     0ch    2    number available
  888.     0eh    2    number used
  889.     10h    4    linear address (region 0)
  890.     14h    4    size in bytes (region 0)
  891.     18h    4    linear address (region 1)
  892.     1ch    4    size in bytes (region 1)
  893.             .
  894.             .
  895.             .
  896.  
  897. If there are page tables contained, the following structure applies:
  898.  
  899.     Offset    Bytes    Meaning
  900.     00h    4    size of region
  901.     04h    4    region offset
  902.     08h    2    region segment
  903.     0ah    2    reserved
  904.     0ch    2    number available
  905.     0eh    2    number used
  906.     10h    4    Page Table Entry 0
  907.     14h    4    Page Table Entry 1
  908.             .
  909.             .
  910.             .
  911.  
  912. Bits 1-12 of a Page Table Entry should be cleared. Bit 0 has to be set if the
  913. page is present and locked.
  914.  
  915.  
  916. Func:    VDS Get Version (AX=8102h)
  917. Call:    INT 4Bh
  918. Input:    AX=8102h
  919.     DX=0
  920. Return:    CY=1 - error
  921.     AL=error code
  922.     CF=0 - ok
  923.     AH=major version number
  924.     AL=minor version number
  925.     BX=product number
  926.     CX=revision number
  927.     DX=flags
  928.     SI:DI=buffer size
  929. Notes:    Flag bits in DX:
  930.         Bit    Meaning
  931.         0    PC/XT Bus System (1Mb addressable)
  932.         1    physical buffer/remap region in 1st Mb
  933.         2    automatic remap enabled
  934.         3    all memory physically contigous
  935.         4-15    reserved
  936.     SI:DI contains maximal size of DMA buffer.
  937.  
  938.  
  939. Func:    VDS Lock DMA Region (AX=8103h)
  940. Call:    INT 4Bh
  941. Input:    AX=8103h
  942.     DX=Flags
  943.     ES:SI=DMA Descriptor
  944. Return:    CY=1 - error
  945.     AL=error code
  946.     CY=0 - ok
  947. Notes:    DX is used as flag register to control the operation.
  948.         Bit    Meaning
  949.         0    reserved (cleared)
  950.         1    copy data to buffer (ignored if bit 2 set)
  951.         2    don't allocate buffer if region not contigous or
  952.             exceeds physical boundaries (bit 4,5)
  953.         3    don't try to automatically remap
  954.         4    region must not exceed 64kb
  955.         5    region must not exceed 128kb
  956.         6-15    reserved (cleared)
  957.     Region Size Field in DDS contains size of maximal contigous memory
  958.     area. If Carry Flag is clear, area is locked and may not be swapped.
  959.     Physical Address and Buffer ID are filled by the function. If Buffer ID
  960.     is 0, no buffer has been allocated.
  961.  
  962.  
  963. Func:    VDS Unlock DMA Region (AX=8104h)
  964. Call:    INT 4Bh
  965. Input:    AX=8104h
  966.     DX=flags
  967.     ES:DI=DMA Descriptor
  968. Return:    CY=1 - error
  969.     AL=error code
  970.     CY=0 - ok
  971. Notes:    Flag bits in DX:
  972.         Bit    Meaning
  973.         0    reserved (cleared)
  974.         1    Copy data from buffer
  975.         2-15    reserved (cleared)
  976.     Region Size, Physical Address and Buffer ID in DDS have to be filled.
  977.  
  978.  
  979. Func:    VDS Scatter/Gather Lock Region (AX=8105h)
  980. Call:    INT 4Bh
  981. Input:    AX=8105h
  982.     BX=page offset (not sure about it)
  983.     DX=flags
  984.     ES:DI=DMA Descriptor
  985. Return:    CY=1 - error
  986.     AL=error code
  987.     CY=0 - ok
  988. Notes:    Function is used to lock parts of memory. Useful if parts of memory
  989.     are swapped out.
  990.     Flag bits in DX:
  991.         Bit    Meaning
  992.         0-5    reserved (cleared)
  993.         6    return EDDS with page table entries
  994.         7    only lock existing pages, fill not existing pages
  995.             with 0
  996.         8-15    reserved (cleared)
  997.     Region Size, Linear Segment, Linear Offset and Number Available have to
  998.     be set. Region Size Field in EDDS will be filled with size of largest
  999.     contigous memory block. Number Used will be filled with the number of
  1000.     used pages. If bit 6 in DX is set, lower 12 bits of BX should contain
  1001.     offset of first page (not sure about that).
  1002.  
  1003.  
  1004. Func:    VDS Scatter/Gather Unlock Region (AX=8106h)
  1005. Call:    INT 4Bh
  1006. Input:    AX=8106h
  1007.     DX=Flags
  1008.     ES:DI=DMA Descriptor
  1009. Return:    CY=1 - error
  1010.     AL=error code
  1011.     CY=0 - ok
  1012. Notes:    Flag bits in DX:
  1013.         Bit    Meaning
  1014.         0-5    reserved (cleared)
  1015.         6    EDDS contains page table entries
  1016.         7    EDDS may contain not present pages
  1017.         8-15    reserved (cleared)
  1018.     ES:DI contains EDDS initialised by function 8105h.
  1019.  
  1020.  
  1021. Func:    VDS Request DMA Buffer (AX=8107h)
  1022. Call:    INT 4Bh
  1023. Input:    AX=8107h
  1024.     DX=Flags
  1025.     ES:DI=DMA Descriptor
  1026. Return:    CY=1 - error
  1027.     AL=error code
  1028.     CY=0 - ok
  1029. Notes:    Flag bits in DX:
  1030.         Bit    Meaning
  1031.         0    reserved (cleared)
  1032.         1    Copy data to buffer
  1033.         2-15    reserved (cleared)
  1034.     ES:DI contains pointer to DDS. Region Size has to be filled. If bit 1
  1035.     in DX is set, Region Offset and Region Segment have to be filled, too.
  1036.     Function returns Physical Address, Buffer ID and Region Size.
  1037.  
  1038.  
  1039. Func:    VDS Release DMA Buffer (AX=8108h)
  1040. Call:    INT 4Bh
  1041. Input:    AX=8108h
  1042.     DX=flags
  1043.     ES:DI=DMA Descriptor
  1044. Return:    CY=1 - error
  1045.     AL=error code
  1046.     CY=0 - ok
  1047. Notes:    Flag bits in DX:
  1048.         Bit    Meaning
  1049.         0    reserved (cleared)
  1050.         1    copy data from buffer
  1051.         2-15    reserved (cleared)
  1052.     Buffer ID in DDS has to filled. If bit 1 in DX is set, Region Size,
  1053.     Region Offset and Region Segment have to be initialised, too.
  1054.  
  1055.  
  1056. Func:    VDS Copy into DMA Buffer (AX=8109h)
  1057. Call:    INT 4Bh
  1058. Input:    AX=8109h
  1059.     DX=0
  1060.     ES:DI=DMA Descriptor
  1061.     BX:CX=offset
  1062. Return:    CY=1 - error
  1063.     AL=error code
  1064.     CY=0 - ok
  1065. Notes:    BX:CX contains offset into DMA Buffer. ES:DI contains pointer to DDS.
  1066.     Buffer ID, Region Offset, Region Segment and Region Size have to be
  1067.     initialised.
  1068.  
  1069.  
  1070. Func:    VDS Copy out of DMA Buffer (AX=810ah)
  1071. Call:    INT 4Bh
  1072. Input:    AX=810ah
  1073.     DX=0
  1074.     ES:DI=DMA Descriptor
  1075.     BX:CX=offset
  1076. Return:    CY=1 - error
  1077.     AL=error code
  1078.     CY=0 - ok
  1079. Notes:    BX:CX contains offset into DMA Buffer. ES:DI contains pointer to DDS.
  1080.     Buffer ID, Region Offset, Region Segment and Region Size have to be
  1081.     initialised.
  1082.  
  1083.  
  1084. Func:    VDS Disable DMA Translation (AX=810bh)
  1085. Call:    INT 4Bh
  1086. Input:    AX=810bh
  1087.     DX=0
  1088.     BX=DMA channel
  1089. Return:    CY=1 - error
  1090.     AL=error code
  1091.     CY=0 - ok
  1092. Notes:    Function stops DMA transfer on channel BX.
  1093.  
  1094.  
  1095. Func:    VDS Enable DMA Translation (AX=810ch)
  1096. Call:    INT 4Bh
  1097. Input:    AX=810ch
  1098.     DX=0
  1099.     BX=DMA channel
  1100. Return:    CY=1 - error
  1101.     AL=error code
  1102.     CY=0 - ok
  1103. Notes:    Function starts DMA transfer on channel BX.
  1104.  
  1105. -------------------------------------------------------------------------------
  1106.  
  1107. -- THE VIRTUAL CONTROL PROGRAM INTERFACE (VCPI) --
  1108.  
  1109. The Virtual Control Program Interface (VCPI) was the first standard to manage
  1110. memory in a Protected Mode or Virtual 86 Mode environment. In has been founded
  1111. in 1987 by many different companies. (PharLap, Quarterdeck, Qualitas, LOTUS,
  1112. Autodesk and others)
  1113.  
  1114. The communication between the interface and the application is divided into
  1115. a Server and a Client. The program that provides the interface services is
  1116. recognized as the Server. The application will be the Client.
  1117.  
  1118. To call the Server, there are two ways: in Real Mode, you have to use INT 67h,
  1119. in Protected Mode, you have to use a FAR CALL.
  1120.  
  1121. Everytime I speak of page addresses, I mean the common format to address a
  1122. page (bits 31-22=page directory, bits 21-12=directory entry, bits 11-0=offset).
  1123. The offset part of the page address is normally always set to 0.
  1124. [Page Directory
  1125.  
  1126. I am not sure about the information contained here. Please mail me if there are
  1127. errors. I hoped it would be handy for you, so it is included.
  1128.  
  1129.  
  1130. Func:    VCPI Installation Check (INT 67h, AX=DE00h)
  1131. Call:    INT 67h
  1132. Input:    AX=DE00h
  1133. Return:    AH=0 - VCPI is available
  1134.     BH=major version number
  1135.     BL=minor version number
  1136.     AH!=0 - VCPI not available
  1137. Notes:    Some docs say that AH=84h on return if VCPI isn't available, but EMM
  1138.     is enabled.
  1139.  
  1140.  
  1141. Func:    VCPI Get Protected Mode Interface (INT 67h, AX=DE01h)
  1142. Call:    INT 67h
  1143. Input:    AX=DE01h
  1144.     DS:SI=pointer to descriptors
  1145.     ES:DI=pointer to client pages
  1146. Return:    AH=0 - ok
  1147.     DI=pointer into page directory
  1148.     EBX=offset of entry point
  1149.     AH!=0 - error
  1150. Notes:    To call the Server in Protected Mode, you have to use the returned
  1151.     address. The memory at DS:SI has to have enough space for three GDT
  1152.     Descriptors, the first descriptor will be filled with the VCPI
  1153.     code segment. Use a FAR CALL into this segment, offset EBX, to reach
  1154.     the Server dispatcher. The space has to be in the first page in the
  1155.     applications code segment. ES:DI has to contain a pointer to a list of
  1156.     pages used by the Client. In DI, a pointer to the first unused page is
  1157.     returned.
  1158.  
  1159.  
  1160. Func:    VCPI Get Maximum Physical Memory (INT 67h, AX=DE02h)
  1161. Call:    INT 67h
  1162. Input:    AX=DE02h
  1163. Return:    AH=0 - ok
  1164.     EDX=page address
  1165.     AH!= - error
  1166. Notes:    EDX contains the address of the highest 4kb page in memory. The lowest
  1167.     12 bits are set to zero. Some Clients are using this call to
  1168.     initialize their data structures.
  1169.  
  1170.  
  1171. Func:    VCPI Get Number of Free 4K Pages (INT 67h / CALL FAR, AX=DE03h)
  1172. Call:    INT 67h / CALL FAR
  1173. Input:    AX=DE03h
  1174. Return:    AH=0 - ok
  1175.     EDX=number of free pages
  1176.     AH!=0 - error
  1177. Notes:    The call returns the number of free pages that are available for all
  1178.     tasks. This function is available in Protected Mode, too. (CALL FAR...)
  1179.  
  1180.  
  1181. Func:    VCPI Allocate a 4K Page (INT 67h / CALL FAR, AX=DE04h)
  1182. Call:    INT 67h / CALL FAR
  1183. Input:    AX=DE04h
  1184. Return:    AH=0 - ok
  1185.     EDX=page address
  1186.     AH!=0 - error
  1187. Notes:    The function allocates a 4K page for the Client. The lowest 12 bits of
  1188.     EDX are set to 0. This function is available in Protected Mode, too.
  1189.  
  1190.  
  1191. Func:    VCPI Free a 4K Page (INT 67h / CALL FAR, AX=DE05h)
  1192. Call:    INT 67h / CALL FAR
  1193. Input:    AX=DE05h
  1194.     EDX=page address
  1195. Return:    AH=0 - ok
  1196.     AH!=0 - error
  1197. Notes:    The page has to be allocated by function DE04h. The lowest 12 bits of
  1198.     EDX are set to 0. This function is available in Protected Mode, too.
  1199.  
  1200.  
  1201. Func:    VCPI Get Physical Address of Page in First MB (INT 67h, AX=DE06h)
  1202. Call:    INT 67h
  1203. Input:    AX=DE06h
  1204.     CX=page number
  1205. Return:    AH=0 - ok
  1206.     EDX=page address
  1207.     AH!=0
  1208. Notes:    The function returns the address of a page in the first MB. The lowest
  1209.     12 bits of EDX are set to zero. The page number in CX is the address of
  1210.     the page SHL 12. (This is written in my VCPI docs, but quite illogical,
  1211.     because then there are only the 4 highest bits available for the page
  1212.     number)
  1213.  
  1214.  
  1215. Func:    VCPI Read CR0 (INT 67, AX=DE07h)
  1216. Call:    INT 67h
  1217. Input:    AX=DE07h
  1218. Return:    AH=0 - ok
  1219.     EBX=CR0
  1220.     AH!=0 - error
  1221. Notes:    The function returns CR0 in EBX because MOV xxx,CR0 isn't allowed in
  1222.     V86 Mode. However, EMM386 and QEMM simulate this instruction and you
  1223.     don't have to use an interrupt call.
  1224.  
  1225.  
  1226. Func:    VCPI Read Debug Register (INT 67h, AX=DE08h)
  1227. Call:    INT 67h
  1228. Input:    AX=DE08h
  1229.     ES:DI=pointer to buffer
  1230. Return:    AH=0 - ok
  1231.     AH!=0 - error
  1232. Notes:    ES:DI has to provide enough space for 8 entries, every entry has a size
  1233.     of 4 bytes. The function stores DR0, DR1, ..., DR7. DR4 and DR5 are
  1234.     unused.
  1235.  
  1236.  
  1237. Func:    VCPI Set Debug Register (INT 67h, AX=DE09h)
  1238. Call:    INT 67h
  1239. Input:    AX=DE09h
  1240.     ES:DI=pointer to buffer
  1241. Return:    AH=0 - ok
  1242.     AH!=0 - error
  1243. Notes:    ES:DI has to point to a table with 8 entries, every entry has a size of
  1244.     4 bytes. The function loads DR0, DR1, ..., DR7. DR4 and DR5 are unused.
  1245.  
  1246.  
  1247. Func:    VCPI Get 8259 Interrupt Vector Mappings (INT 67h, AX=DE0Ah)
  1248. Call:    INT 67h
  1249. Input:    AX=DE0Ah
  1250. Return:    AH=0 - ok
  1251.     BX=1st PIC Vector Map
  1252.     CX=2nd PIC Vector Map
  1253.     AH!=0 - error
  1254. Notes:    The Server returns the mapping from the Master PIC in BX (start of
  1255.     first 8 hardware IRQs) and the mapping from the Slave PIC in CX (start
  1256.     of next 8 hardware IRQs). If there's no Slave PIC installed, CX is
  1257.     undefined.
  1258.  
  1259.  
  1260. Func:    VCPI Set 8259 Interrupt Mappings (INT 67h, AX=DE0Bh)
  1261. Call:    INT 67h
  1262. Input:    AX=DE0Bh
  1263.     BX=1st PIC Vector Map
  1264.     CX=2nd PIC Vector Map
  1265. Return:    AH=0 - ok
  1266.     AH!=0 - error
  1267. Notes:    Master PIC is programmed with BX, Slave PIC is programmed with CX.
  1268.     Interrupts have to be disabled before calling this function.
  1269.  
  1270.  
  1271. Func:    VCPI Switch to Protected Mode (INT 67h, AX=DE0Ch)
  1272. Call:    INT 67h
  1273. Input:    AX=DE0Ch
  1274.     ESI=pointer to data structure
  1275. Return:    AH=0 - ok
  1276.     AH!=0 - error
  1277. Notes:    The data structure has to be setup by the Client in the first MB. ESI
  1278.     has to contain the linear address of it. Structure as follows:
  1279.         Offset    Bytes    Meaning
  1280.         00h    4    new value for CR3
  1281.         04h    4    linear address in first MB of value for
  1282.                 GDT register (6 bytes)
  1283.         08h    4    linear address in first MB of value for
  1284.                 IDT register (6 bytes)
  1285.         0Ch    2    selector for LDT register
  1286.         0Eh    2    selector for Task Register
  1287.         10h    4    EIP of Protected Mode entry point
  1288.         14h    2    CS of Protected Mode entry point
  1289.     The function loads GDTR, IDTR, LDTR and TR. SS:ESP has to point to a
  1290.     stack with at least 16 bytes available on entry. EAX, ESI, DS, ES, FS
  1291.     and GS are destroyed.
  1292.     The CPU continues execution in Protected Mode at address CS:EIP
  1293.     specified in the table.
  1294.     Interrupts are disabled on return.
  1295.  
  1296.  
  1297. Func:    Switch from Protected Mode to V86 Mode (CALL FAR, AX=DE0Ch)
  1298. Call:    CALL FAR
  1299. Input:    AX=DE0Ch
  1300.     DS=segment selector
  1301. Return:    ---
  1302. Notes:    The stack has to be shifted in the first MB on entry. DS has to contain
  1303.     a selector for a segment that includes the address area returned by
  1304.     function DE01h.
  1305.     The function switches to V86 mode. Interrupt have to be disabled.
  1306.     GDTR, IDTR, LDTR and TR are initialised by the Server. SS:ESP has to
  1307.     contain the following structure:
  1308.         Offset    Meaning
  1309.         -28h    GS
  1310.         -24h    FS
  1311.         -20h    DS
  1312.         -1Ch    ES
  1313.         -18h    SS
  1314.         -14h    ESP
  1315.         -10h    reserved
  1316.         -0Ch    CS
  1317.         -08h    EIP
  1318.          00h    return address
  1319.     EAX is destroyed.
  1320.  
  1321. -------------------------------------------------------------------------------
  1322.  
  1323. -- THE DOS PROTECTED MODE INTERFACE (DPMI) --
  1324.  
  1325. After the successful VCPI standard, a new standard was founded: DPMI. With the
  1326. DPMI, some "bugs" of VCPI were removed, for example VCPI allowed to run a task
  1327. on CPL=0. DPMI has been published in 1990 by Microsoft and Intel with
  1328. version 0.9, in 1991 version 1.0 has been published.
  1329.  
  1330. All DPMI functions are reentrant. Many implementations use a VMM, so be sure to
  1331. lock your memory if you don't want it to be swapped to disk.
  1332.  
  1333. Every DPMI task uses four stacks: - Protected Mode Application Stack (CPL=0)
  1334.                   - Locked Protected Mode Stack
  1335.                   - Real Mode Stack
  1336.                   - DPMI Host Stack (CPL=0).
  1337.  
  1338. The Protected Mode Stack is used by the Client when switching from Real- to
  1339. Protected Mode. The Locked Protected Mode Stack is used by the DPMI Server to
  1340. simulate hardware IRQs. The Real Mode Stack is used for Real Mode IRQs and the
  1341. DPMI Host Stack is used by (who else could it have been... ;) ) the DPMI Host.
  1342.  
  1343. Unlike VCPI, all Protected Mode function can be reached via INT 31h.
  1344.  
  1345. I didn't include the interface here, because it is nearly 70 (140 on screen)
  1346. pages "small". I'd really like to know if someone knows a source, but if
  1347. there's enough demand, I'll include the whole interface here.
  1348.  
  1349. -------------------------------------------------------------------------------
  1350.  
  1351. -- OTHER TEXTS --
  1352.  
  1353. This document has been created to be part of the comp.lang.asm.x86 FAQ,
  1354. section 13 (Real Mode/Protected Mode).
  1355. Jerzy Tarasiuk (the author of the directly in the FAQ included text) and I
  1356. cooperate on writing and extending the FAQ contribution. (Again, please mail us
  1357. about something you want to have included, I LOVE RECEIVING MAILS! ;) )
  1358.  
  1359. His texts are on zfja-gate.fuw.edu.pl:cpu/protect.mod/* . There is also a
  1360. listserver, to get the files mail to listserv@zfja-gate.fuw.edu.pl with the
  1361. body of the letter containing
  1362.  
  1363. GET/CPU/PROTECT.MOD/*.ZIP
  1364. GET/CPU/PROTECT.MOD/*.TXT
  1365.  
  1366. to get all of his files (about 200k). Subjects are switching from Real Mode
  1367. to Protected Mode, V86 Mode, basic multitasking and using Protected Mode with
  1368. Turbo Pascal (NOT the Turbo Pascal DPMI stuff!!).
  1369.  
  1370. Jerzy's internet address is: JT@zjfa-gate.fuw.edu.pl
  1371.  
  1372. Another really good choice is Tran's Protected Mode package. I don't know which
  1373. ftp server has this file available, but it should be on x2ftp.oulu.fi
  1374. (teeri.oulu.fi). However, if you have Fidonet access, you can request it at
  1375. 2:2426/2030 (file PMC100.ZIP). This file contains a complete DOS Extender for
  1376. BC++ 4.0 (can be used with Watcom, too), including all sources.
  1377.  
  1378. For those German speaking people out there, I suggest reading the following
  1379. book:
  1380.  
  1381. Author:        Dr. Wolfgang Matthes
  1382. Title:        Intel's i486
  1383.         Architektur und Befehlsbeschreibung
  1384.         der xxx86er-Familie
  1385. Published by:    Elektor
  1386. ISBN:        3-928051-29-6
  1387.  
  1388. -------------------------------------------------------------------------------
  1389.  
  1390. -- THOUGHTS AND THANK YOUS --
  1391.  
  1392. At first I like to thank Jerzy Tarasiuk who agreed to cooperate with me in
  1393. writing the comp.lang.asm.x86 FAQ Section 13. He gave me useful corrections.
  1394.  
  1395. Second, a big thanks goes out to Jouni Miettounen and the whole x2ftp crew for
  1396. approving this text at x2ftp.oulu.fi. It is the first text for the net I wrote
  1397. in my whole life and I didn't expect that something like this could be done
  1398. without any problems at all.
  1399.  
  1400. And, of course, I would like to thank Jason Steinberg <Zydex@aol.com> and Poom
  1401. Malakul Na Ayudhya <POOM@bhpp.moph.go.th> (the first two guys who wrote to me
  1402. about this text!) and all the others that helped me getting rid of some bugs.
  1403.  
  1404. Then I want to say something that flies around in my head for quite a while:
  1405.  
  1406. When I got my internet access, one of the first things I saw was Raymond Moons
  1407. frequently posting of the comp.lang.asm.x86 FAQ. I looked through the subjects
  1408. and saw that the Real Mode/Protected Mode section was open. As for that time I
  1409. was writing quite a lot of stuff for Protected Mode, I mailed him asking if I
  1410. could make a little contribution.
  1411.  
  1412. The result was this text, that I hope will give you answers to many of your
  1413. questions. All information contained here has been collected from only one
  1414. processor description, thousands of little notes and millions of hardware
  1415. crashes caused by my never-ending stupid tries making it work. I give it to you
  1416. now without asking for anything (well, the postcard would be nice :) ).
  1417.  
  1418. So, please, if you discover something new, don't post on the net something
  1419. like
  1420.     "I discovered a totally new mind-blasting thingy, but you won't
  1421.      get the sources dudes, 'cuz they're mine..."
  1422. It is so stupid!
  1423.  
  1424. Share your experiences with others so they can learn from you! Remember how
  1425. hard it was for you learning to code really good programs?
  1426.  
  1427. I hear them saying: Money! I won't get money by sharing my ideas!
  1428.  
  1429. Wrong. Again: WRONG! You don't have to publish your rip-roaringly fast
  1430. 3D-engine, your totally cool multitasking environment or your mind-blasting
  1431. DTP software you just wrote.
  1432.  
  1433. All I ask from you is to share the concepts. Give away your algorithms, sample
  1434. codes or whatever you've made. Let the dudes who really want to get on with it
  1435. do the work. You'll be already on other topics when others just finished
  1436. understanding what you meant. You'll be ahead of them. And the first gets the
  1437. money.
  1438.